/*
 * CTimerTaskQueue.cpp
 *
 *  Created on: 2024年8月21日
 *      Author: chuan
 */

#include "CTimerTaskQueue.h"
#include <algorithm>
#include "ClockTime.h"


struct IsThisID : public std::binary_function<CTimerTaskQueue::TimerTask, TimerTaskQueue::TimerID, bool >
{
	bool operator () (const CTimerTaskQueue::TimerTask& task, const TimerTaskQueue::TimerID& id) const
	{
		return (task.m_id == id);
	}
};

struct ExpireComparator : public std::binary_function< CTimerTaskQueue::TimerTask, CTimerTaskQueue::TimerTask, bool>
{
	bool operator () (const CTimerTaskQueue::TimerTask& first, const CTimerTaskQueue::TimerTask& task) const
	{
		return (first.m_expire < task.m_expire);
	}
};



CTimerTaskQueue::CTimerTaskQueue()
{
	setStackSize(1024 * 1024 * 2);
}

CTimerTaskQueue::~CTimerTaskQueue()
{
	close();
}

bool CTimerTaskQueue::open()
{
	if (isOpen())
	{
		return true;
	}

	bool got = start();

	if (got)
	{
		setName("CTimerTaskQueue");
	}

	return got;
}

void CTimerTaskQueue::close()
{
	if (isRunning())
	{
		stop();
	}

	clear();
}

bool CTimerTaskQueue::isOpen()
{
	return isRunning();
}

bool CTimerTaskQueue::addTimer(TimerID id, uint32_t ms, TimerHandler handler)
{
	if (!handler)
	{
		return false;
	}

	TimerTask task(id, handler, ms, true);
	return add(task);
}

bool CTimerTaskQueue::addElapse(TimerID id, uint32_t ms, TimerHandler handler)
{
	if (!handler)
	{
		return false;
	}

	TimerTask task(id, handler, ms, false);
	return add(task);
}

bool CTimerTaskQueue::cancel(TimerID id)
{
	return remove(id);
}

void CTimerTaskQueue::clear()
{
	std::lock_guard<std::mutex> lock(m_mutex);
	m_taskList.clear();
}


bool CTimerTaskQueue::add(TimerTask& task)
{
	bool found = remove(task.m_id);

	task.updateExpire(getCurTime());
	insertTask(task);

	m_event.post();

	return !found;
}

void CTimerTaskQueue::insertTask(TimerTask& task)
{
	std::lock_guard<std::mutex> lock(m_mutex);
	iterator it = findWithExpire(task.m_expire);
	m_taskList.insert(it, task);
}

bool CTimerTaskQueue::runTask(TimerTask& task)
{
	if (task.m_handler)
	{
		return task.m_handler(task.m_id, task.m_interval);
	}
	return false;
}

bool CTimerTaskQueue::checkoutFront(TimerTask& task, int64_t& timeout)
{
	bool found = false;
	timeout = -1;
	std::lock_guard<std::mutex> lock(m_mutex);
	if (!m_taskList.empty())
	{
		int64_t tmNow = getCurTime();
		task = m_taskList.front();
		if (task.m_expire <= (tmNow + DEVIATION))
		{
			m_taskList.pop_front();
			timeout = 0;
			found = true;
		}
		else
		{
			timeout = (task.m_expire - tmNow);
		}
	}
	return found;
}

int64_t CTimerTaskQueue::checkExpire()
{
	int64_t timeout = -1;
	TimerTask task;
	if (checkoutFront(task, timeout))
	{
		bool done = runTask(task);

		if (done && task.m_isRepeat)
		{
			int64_t expire = task.updateExpire();
			if (expire < getCurTime())
			{
				task.updateExpire(getCurTime());
			}

			insertTask(task);
		}

		timeout = 0;
	}
	return timeout;
}

int CTimerTaskQueue::run()
{
	int64_t timeout = -1;
	while (!m_canExit)
	{
		//Time watch;
		//std::cout << "timeout: " << timeout << std::endl;
		// it is not precise,error range is about [0-20] in win32
		timedwait(timeout);
		if (!m_canExit)
		{
			timeout = checkExpire();
		}
	}
	return 0;
}

void CTimerTaskQueue::doStop()
{
	m_event.post();
}

CTimerTaskQueue::iterator CTimerTaskQueue::find(const TimerID& id)
{
	return std::find_if(m_taskList.begin(), m_taskList.end(),
		std::bind2nd(IsThisID(), id));
}

bool CTimerTaskQueue::remove(const TimerID& id)
{
	std::lock_guard<std::mutex> lock(m_mutex);

	bool found = false;
	iterator it = find(id);
	if (it != m_taskList.end())
	{
		m_taskList.erase(it);
		found = true;
	}
	return found;
}

CTimerTaskQueue::iterator CTimerTaskQueue::findWithExpire(int64_t expire)
{
	TimerTask first;
	first.m_expire = expire;
	return std::upper_bound(m_taskList.begin(), m_taskList.end(),
		first, ExpireComparator());
}

int64_t CTimerTaskQueue::getCurTime()
{
	return comn::ClockTime::getTime();
}

bool CTimerTaskQueue::timedwait(int64_t ms)
{
	if (ms == 0)
	{
		return true;
	}
	return m_event.timedwait((int)ms);
}

